
<template>

  <div class="form-panel">

    <p class="hint-text" v-show="data.list.length === 0">
      从左侧选择组件拖动到这里
    </p>
    <Form
        :label-width="data.config.labelWidth "
        class="a-form-box form-build"
        :label-position="data.config.labelPosition"
        :hide-required-mark="data.config.hideRequiredMark"
        ref="form"
        :style="data.config.customStyle"
        :size="data.config.size"
    >
      <draggable
          tag="div"
          class="draggable-box"
          v-bind="{
          group: 'form-draggable',
          ghostClass: 'moving',
          animation: 180,
          handle: '.drag-move'
        }"
          :force-fallback="true"
          v-model="data.list"
          @add="deepClone"
          @start="dragStart($event, data.list)"
      >
        <transition-group tag="div" name="list" class="list-main">
          <layoutItem
              class="drag-move"
              v-for="record in data.list"
              :key="record.key"
              :record="record"
              :config="data.config"
              :selectItem.sync="selectItem"
              :startType="startType"
              :insertAllowedType="insertAllowedType"
              :hideModel="hideModel"
              @dragStart="dragStart"
              @handleSelectItem="handleSelectItem"
              @handleCopy="handleCopy"
              @handleDetele="handleDetele"
              @handleColAdd="handleColAdd"
              @handleShowRightMenu="handleShowRightMenu"
          />
        </transition-group>
      </draggable>
    </Form>

    <div
        v-show="showRightMenu"
        :style="{ 'top': menuTop + 'px', 'left': menuLeft + 'px' }"
        class="right-menu"
        id="rightMenu"
    >
      <ul>
        <li @click="handleSettingStyle"><i class="el-icon-magic-stick"/>样式配置</li>
        <hr>
        <template v-if="isMergeCol">
          <li @click="handleDropMerge"><i class="el-icon-delete"/>解除合并</li>
          <hr>
        </template>
        <li @click="handleDownMerge"><i class="el-icon-bottom"/>向下合并</li>
        <li @click="handleRightMerge"><i class="el-icon-right"/>向右合并</li>
        <li @click="handleAddCol"><i class="el-icon-zoom-in"/>增加一列</li>
        <li @click="handleAddRow"><i class="el-icon-zoom-in"/>增加一行</li>
        <hr>
        <li @click="handleRemoveRow"><i class="el-icon-zoom-out"/>删除当前行</li>
        <li @click="handleRemoveCol"><i class="el-icon-zoom-out"/>删除当前列</li>

      </ul>
    </div>

    <Modal
        v-model="styleVisible"
        title="表内单元格样式配置"
        style="top:20px;"
        :append-to-body="true"
    >
      <Form :model="tdStyle" :label-width="80">
        <FormItem label="class">
          <Input v-model="tdStyle.class" placeholder="请输入class名称"/>
        </FormItem>
        <FormItem label="style">
          <Input type="textarea" :rows="3" v-model="tdStyle.style" placeholder="请输入css样式"/>
        </FormItem>
      </Form>
      <span slot="footer" class="dialog-footer">
        <Button @click="styleVisible = false">取 消</Button>
        <Button type="primary" @click="settingStyle">确 定</Button>
      </span>
    </Modal>


  </div>
</template>
<script>
import draggable from 'vuedraggable';
import layoutItem from './layout';
import cloneDeep from 'lodash/cloneDeep';

export default {
  name: 'FormPanel',
  data() {
    return {
      form: this.$refs.form,
      insertAllowedType: [
        'input',
        'textarea',
        'number',

        'select',
        'batch',
        'checkbox',
        'radio',
        'date',
        'time',
        'rate',
        'slider',
        'uploadFile',
        'uploadImg',
        'cascader',
        'treeSelect',
        'switch',
        'text',
        'html',
        'control',
      ],
      rightMenuSelectValue: {},
      showRightMenu: false,
      isMergeCol: false,
      menuTop: 0,
      menuLeft: 0,
      trIndex: 0,
      tdIndex: 0,

      styleVisible: false,
      tdStyle: {
        style: '',
        class: '',
      },
    };
  },
  props: {
    noModel: {
      type: Array,
      required: true,
    },
    startType: {
      type: String,
      required: true,
    },
    data: {
      type: Object,
      required: true,
    },
    selectItem: {
      type: Object,
      default: () => {
      },
    },
    hideModel: {
      type: Boolean,
      default: false,
    },
  },
  components: {
    draggable,
    layoutItem,
  },
  methods: {
    deepClone(evt) {
      const newIndex = evt.newIndex;


      const listString = JSON.stringify(this.data.list);
      this.data.list = JSON.parse(listString);


      if (this.data.list && this.data.list.length > newIndex) {
        delete this.data.list[newIndex].icon;
        delete this.data.list[newIndex].component;
        this.$emit('handleSetSelectItem', this.data.list[newIndex]);
      }

    },
    handleColAdd(evt, columns, isCopy = false) {

      const newIndex = evt.newIndex;

      const key = columns[newIndex].type + '_' + new Date().getTime();
      if (columns[newIndex].key === '' || isCopy) {
        this.$set(columns, newIndex, {
          ...columns[newIndex],
          key,
          model: key,
        });
        if (this.noModel.includes(columns[newIndex].type)) {

          delete columns[newIndex].model;
        }
        if (typeof columns[newIndex].options !== 'undefined') {

          const optionsStr = JSON.stringify(columns[newIndex].options);
          columns[newIndex].options = JSON.parse(optionsStr);
        }
        if (typeof columns[newIndex].rules !== 'undefined') {

          const rulesStr = JSON.stringify(columns[newIndex].rules);
          columns[newIndex].rules = JSON.parse(rulesStr);
        }
        if (typeof columns[newIndex].list !== 'undefined') {

          columns[newIndex].list.forEach(t => {
            t.model = t.model + 1;
            t.key = t.key + 1;
          });
        }
        if (typeof columns[newIndex].columns !== 'undefined') {

          const columnsStr = JSON.stringify(columns[newIndex].columns);
          columns[newIndex].columns = JSON.parse(columnsStr);

          columns[newIndex].columns.forEach(item => {
            if (item.list && item.list.length > 0) {
              item.list.forEach(t => {
                t.model = t.model + 1;
                t.key = t.key + 1;
              });
            }

          });
        }
        if (columns[newIndex].type === 'table') {

          const trsStr = JSON.stringify(columns[newIndex].trs);
          columns[newIndex].trs = JSON.parse(trsStr);

          columns[newIndex].trs.forEach(item => {

            item.tds.forEach(val => {
              if (val.list && val.list.length > 0) {
                val.list.forEach(t => {
                  t.model = t.model + 1;
                  t.key = t.key + 1;
                });
              }

            });
          });
        }
      }

      const listString = JSON.stringify(columns[newIndex]);
      columns[newIndex] = JSON.parse(listString);
      this.$emit('handleSetSelectItem', columns[newIndex]);
    },
    dragStart(evt, list) {

      this.$emit('handleSetSelectItem', list[evt.oldIndex]);
    },
    handleSelectItem(record) {

      this.$emit('handleSetSelectItem', record);
    },
    handleCopy(isCopy = true, data) {
      const traverse = array => {
        array.forEach((element, index) => {
          if (element.key === this.selectItem.key) {
            if (isCopy) {

              array.splice(index + 1, 0, cloneDeep(element));
            } else {

              array.splice(index + 1, 0, cloneDeep(data));
            }

            const evt = {
              newIndex: index + 1,
            };
            this.handleColAdd(evt, array, true);
            return;
          }
          if (element.type === 'grid') {

            element.columns.forEach(item => {
              traverse(item.list);
            });
          } else if (element.type === 'batch' || element.type === 'control') {

            traverse(element.list);
          } else if (element.type === 'table') {

            element.trs.forEach(item => {
              item.tds.forEach(val => {
                traverse(val.list);
              });
            });
          }

        });
      };
      traverse(this.data.list);
    },
    handleDetele() {

      const traverse = array => {
        array = array.filter((element, index) => {
          if (element.type === 'grid') {

            element.columns.forEach(item => {
              item.list = traverse(item.list);
            });
          } else if (element.type === 'control' || element.type === 'batch') {

            element.list = traverse(element.list);
          } else if (element.type === 'table') {

            element.trs.forEach(item => {
              item.tds.forEach(val => {
                val.list = traverse(val.list);
              });
            });
          }
          if (element.key !== this.selectItem.key) {
            return true;
          } else {
            if (array.length === 1) {
              this.handleSelectItem({key: ''});
            } else if (array.length - 1 > index) {
              this.handleSelectItem(array[index + 1]);
            } else {
              this.handleSelectItem(array[index - 1]);
            }
            return false;
          }
        });
        return array;
      };

      this.data.list = traverse(this.data.list);
    },

    handleDropMerge() {

      const td = this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex];

      const colspan = td.colspan;
      const rowspan = td.rowspan;

      if (td && (colspan > 1 || rowspan > 1)) {
        this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex].colspan = 1;
        this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex].rowspan = 1;


        let cols = [];
        if (colspan > 1) {
          for (var i = 0; i < colspan - 1; i++) {
            cols.push({colspan: 1, rowspan: 1, list: []});
          }

          this.rightMenuSelectValue.trs[this.trIndex].tds.splice(this.tdIndex + 1, 0, ...cols);

        }
        if (rowspan > 1) {

          cols.push({colspan: 1, rowspan: 1, list: []});

          for (var j = this.trIndex + 1; j < this.trIndex + rowspan; j++) {

            this.rightMenuSelectValue.trs[j].tds.splice(this.tdIndex + 1, 0, ...cols);
          }

        }


      }


    },
    handleDownMerge() {

      if (
          this.rightMenuSelectValue.trs.length -
          this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex]
              .rowspan <=
          this.trIndex
      ) {
        this.$message.error('当前是最后一行，无法向下合并');
        return false;
      }


      let rows = 0;
      this.rightMenuSelectValue.trs[this.trIndex].tds.forEach(
          (element, index) => {
            if (index >= this.tdIndex) {
              return false;
            }
            if (
                element.rowspan >
                this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex]
                    .rowspan
            ) {
              rows += 1;
            }
          },
      );
      if (
          this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex]
              .colspan !==
          this.rightMenuSelectValue.trs[this.trIndex + 1].tds[this.tdIndex - rows]
              .colspan
      ) {
        this.$message.error('当前表格无法向下合并');
        return false;
      }


      let rowspan = this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex].rowspan;


      const mergeRowIndex = this.trIndex + rowspan;


      const mergeRow = this.rightMenuSelectValue.trs[mergeRowIndex];
      const mergeCol = mergeRow.tds[this.tdIndex];


      this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex].rowspan = rowspan + mergeCol.rowspan;


      this.rightMenuSelectValue.trs[
      this.trIndex + rowspan
          ].tds = this.rightMenuSelectValue.trs[this.trIndex + rowspan].tds.filter(
          (item, index) => index != this.tdIndex,
      );

    },
    handleRightMerge() {

      const sumCols = this.rightMenuSelectValue.trs[this.trIndex].tds
          .map(item => item.colspan)
          .reduce( (partial, value)=> {
            return partial + value;
          });
      if (
          sumCols -
          this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex]
              .colspan <=
          this.tdIndex
      ) {
        this.$message.error('当前是最后一列，无法向右合并');
        return false;
      }
      if (
          this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex]
              .rowspan !==
          this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex + 1]
              .rowspan
      ) {
        this.$message.error('当前表格无法向右合并');
        return false;
      }

      this.rightMenuSelectValue.trs[this.trIndex].tds[
          this.tdIndex
          ].colspan += this.rightMenuSelectValue.trs[this.trIndex].tds[
      this.tdIndex + 1
          ].colspan;

      this.rightMenuSelectValue.trs[
          this.trIndex
          ].tds = this.rightMenuSelectValue.trs[this.trIndex].tds.filter(
          (item, index) => {
            return index !== this.tdIndex + 1;
          },
      );

    },
    handleAddCol() {
      this.rightMenuSelectValue.trs.forEach(item => {
        item.tds.splice(this.tdIndex + 1, 0, {
          colspan: 1,
          rowspan: 1,
          list: [],
        });
      });
    },
    handleAddRow() {

      const sumCols = this.rightMenuSelectValue.trs[0].tds
          .map(item => item.colspan)
          .reduce((partial, value)=> {
            return partial + value;
          });
      const rowJson = {tds: []};
      for (let i = 0; i < sumCols; i++) {
        rowJson.tds.push({
          colspan: 1,
          rowspan: 1,
          list: [],
        });
      }
      this.rightMenuSelectValue.trs.splice(this.trIndex + 1, 0, rowJson);
    },
    handleRemoveRow() {
      const tri = this.trIndex;
      const len = this.rightMenuSelectValue.trs.length;
      if (len <= 1) {
        this.$message.error('当前是最后一行,无法删除');
        return;
      }
      this.rightMenuSelectValue.trs.splice(tri, 1);

    },
    handleRemoveCol() {
      const tdi = this.tdIndex;

      for (let i in this.rightMenuSelectValue.trs) {
        let tds = this.rightMenuSelectValue.trs[i].tds;
        if (tds.length <= 1) {
          this.$message.error('当前只剩下最后一列,无法删除');
          return;
        }
      }

      this.rightMenuSelectValue.trs.forEach(t => {
        t.tds.splice(tdi, 1);
      });

    },
    handleSettingStyle() {
      const td = this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex];

      const class_ = td.class;
      const style_ = td.style;

      this.tdStyle.style = style_;
      this.tdStyle.class = class_;

      this.styleVisible = true;

    },
    settingStyle() {
      const td = this.rightMenuSelectValue.trs[this.trIndex].tds[this.tdIndex];
      td.style = this.tdStyle.style;
      td.class = this.tdStyle.class;

      this.rightMenuSelectValue.trs[this.trIndex].tds.splice(this.tdIndex, 1, td);

      this.styleVisible = false;

    },
    handleShowRightMenu(e, val, trIndex, tdIndex, mergeCol) {
      e.stopPropagation();
      this.showRightMenu = true;


      let height = 210;
      let width = 280;

      const bodyHeight = document.body.clientHeight;
      const bodyWidth = document.body.clientWidth;


      if (e.clientY + height > bodyHeight) {
        this.menuTop = e.clientY - height;
      } else {
        this.menuTop = e.clientY;
      }

      if (e.clientX + width > bodyWidth) {
        this.menuLeft = e.clientX - width;
      } else {
        this.menuLeft = e.clientX + 20;
      }



      this.activeArr = [val];
      this.rightMenuSelectValue = val;
      this.trIndex = trIndex;
      this.tdIndex = tdIndex;


      this.isMergeCol = mergeCol;


      return false;
    },
    handleRemoveRightMenu() {

      this.showRightMenu = false;
    },
  },
  mounted() {

    document.addEventListener('click', this.handleRemoveRightMenu, true);
    document.addEventListener('contextmenu', this.handleRemoveRightMenu, true);
  },
  destroyed() {

    document.removeEventListener('click', this.handleRemoveRightMenu, true);
    document.removeEventListener(
        'contextmenu',
        this.handleRemoveRightMenu,
        true,
    );
  },
};
</script>
